#include "CalendarModel.h"
#include <QLocale>
#include <QDebug>

CalendarModel::CalendarModel(QObject *parent)
    : QAbstractTableModel(parent)
{
    m_date = QDate::currentDate();
    m_minimumDate = QDate::fromJulianDay(1);
    m_maximumDate = QDate(7999, 12, 31);
    m_shownYear = m_date.year();
    m_shownMonth = m_date.month();
    m_firstDay = Qt::Monday;
    m_horizontalHeaderFormat = SHORT_DAY_NAMES;
    m_isWeekNumbersShown = true;
    m_firstColumn = 1;
    m_firstRow = 1;
    m_weekendList << Qt::Saturday << Qt::Sunday;

//    QHash<int, QByteArray> roles;
//    roles[DATE] = "date";
//    roles[DAY_OF_ANOTHER_MONTH] = "anotherMonthDay";
//    setRoleNames(roles);

    updateMonthSchedule();
}

void CalendarModel::showMonth(int year, int month)
{
    qDebug () << "showMonth:" << month << "," << year;
    if (m_shownYear == year
        && m_shownMonth == month)
    {
        return;
    }
    m_shownYear = year;
    m_shownMonth = month;
    updateMonthSchedule();
    internalUpdate();
}

void CalendarModel::internalUpdate()
{
    QModelIndex begin = index(0, 0);
    QModelIndex end = index(rowCount() - 1, columnCount() - 1);
    // перезаполняем модель
    // приведёт к вызову метода data()
    emit dataChanged(begin, end);
    emit headerDataChanged(Qt::Vertical, 0, rowCount() - 1);
    emit headerDataChanged(Qt::Horizontal, 0, columnCount() - 1);
}

void CalendarModel::updateMonthSchedule()
{
    qDebug() << "updateMonthSchedule()";
    m_monthSchedule = EventSchedule::getMonthSchedule(m_shownYear, m_shownMonth);
}

QVariant CalendarModel::data(const QModelIndex &index, int role) const
{
    int row = index.row();
    int column = index.column();

    if (role == DATE) {
        QDate date = dateForCell(row, column);
        if (date.isValid()) {
            return date;
        }
    }

    if (role == DAY_OF_WEEK) {
        if (isHorizontalHeader(row, column)) {
            return dayName(dayOfWeekForColumn(column));
        }
    }

    if (role == WEEK) {
        if (isVerticalHeader(row, column)) {
            QDate date = dateForCell(row, columnForDayOfWeek(Qt::Monday));
            if (date.isValid()) {
                return date.weekNumber();
            }
        }
    }

    if (role == DAY_OF_ANOTHER_MONTH) {
        QDate date = dateForCell(row, column);
        if (date.isValid()) {
            if (date < m_minimumDate
                    || date > m_maximumDate
                    || date.month() != m_shownMonth) {
                return true;
            }
            return false;
        }
    }

    if (role == WEEKEND) {
        Qt::DayOfWeek dayOfWeek = dayOfWeekForColumn(column);
        if (m_weekendList.contains(dayOfWeek)) {
            return true;
        }
        return false;
    }

    if (role == EVENT) {
        QDate date = dateForCell(row, column);
        EventList_t eventList;
//        qDebug() << "m_monthSchedule.size =" << m_monthSchedule.size();
        foreach (const Event &event, m_monthSchedule) {
//            qDebug() << "event" << event;
//            qDebug() << "event.begin().date()=" << event.begin().date().toString("dd-MM-yyyy");
//            qDebug() << "date=" << date.toString("dd-MM-yyyy");
            // 5-6
            if ((event.begin().date() <= date && event.end().date() >= date)
                || event.begin().date() == date)
            {
//                qDebug() << "data()";
//                qDebug() << "event.imagePath():" << event.imagePath();
//                qDebug() << "event:" << event;
                eventList.append(event);
            }
        }
        if (!eventList.isEmpty()) {
            QVariant result;
            result.setValue(eventList);
            return result;
        }
//        qDebug() << "eventList.size =" << eventList.size();
    }

    return QVariant();
}

bool CalendarModel::isHorizontalHeader(int row, int column) const
{
    if (m_horizontalHeaderFormat != NO_HORIZONTAL_HEADER
        && row == HEADER_ROW
        && column >= m_firstColumn
        && column < columnCount())
    {
        return true;
    }
    return false;
}

bool CalendarModel::isVerticalHeader(int row, int column) const
{
    if (m_isWeekNumbersShown
        && column == HEADER_COLUMN
        && row >= m_firstRow
        && row < rowCount())
    {
        return true;
    }
    return false;
}

QDate CalendarModel::dateForCell(int row, int column) const
{
    // проверка на валидность
    if (row < m_firstRow
        || row > rowCount() - 1
        || column < m_firstColumn
        || column > columnCount() - 1)
    {
        return QDate();
    }

    const QDate date = QDate(m_shownYear, m_shownMonth, 1);
    if (!date.isValid()) {
        return QDate();
    }

    const int columnForFirstOfShownMonth = columnForFirstOfMonth(date);
    if (columnForFirstOfShownMonth - m_firstColumn < MIN_DAY_OFFSET) {
        row -= 1;
    }
    const int requestedDay = 7 * (row - m_firstRow) + column - columnForFirstOfShownMonth - date.day() + 1;
    return date.addDays(requestedDay);
}

int CalendarModel::columnForFirstOfMonth(const QDate &date) const
{
    return (columnForDayOfWeek(static_cast<Qt::DayOfWeek>(date.dayOfWeek())) - (date.day() % 7) + 8) % 7;
}

int CalendarModel::columnForDayOfWeek(Qt::DayOfWeek day) const
{
    if (day < 1 || day > 7) {
        return -1;
    }
    int column = int(day) - int(m_firstDay);
    if (column < 0) {
        column += 7;
    }
    return column + m_firstColumn;
}

Qt::DayOfWeek CalendarModel::dayOfWeekForColumn(int column) const
{
    int col = column - m_firstColumn;
    if (col < 0 || col > 6) {
        return Qt::Sunday;
    }
    int day = m_firstDay + col;
    if (day > 7) {
        day -= 7;
    }
    return Qt::DayOfWeek(day);
}

QString CalendarModel::dayName(Qt::DayOfWeek day) const
{
    switch (m_horizontalHeaderFormat) {
    case SINGLE_LETTER_DAY_NAMES: {
        QLocale locale;
        QString standaloneDayName = locale.standaloneDayName(day, QLocale::NarrowFormat);
        if (standaloneDayName == locale.dayName(day, QLocale::NarrowFormat)) {
            return standaloneDayName.left(1);
        }
        return standaloneDayName;
    }
    case SHORT_DAY_NAMES:
        return QLocale().dayName(day, QLocale::ShortFormat);
    case LONG_DAY_NAMES:
        return QLocale().dayName(day, QLocale::LongFormat);
    case NO_HORIZONTAL_HEADER:
        return QString();
    }
    return QString();
}

void CalendarModel::setDate(const QDate &date)
{
    m_date = date;
    if (m_date < m_minimumDate) {
        m_date = m_minimumDate;
    } else if (m_date > m_maximumDate) {
        m_date = m_maximumDate;
    }

    m_shownYear = m_date.year();
    m_shownMonth = m_date.month();
    internalUpdate();
}

void CalendarModel::setMinimumDate(const QDate &date)
{
    if (!date.isValid() || date == m_minimumDate) {
        return;
    }
    m_minimumDate = date;
    if (m_maximumDate < m_minimumDate) {
        setMaximumDate(m_minimumDate);
    }
    if (m_date < m_minimumDate) {
        setDate(m_minimumDate);
    }
}

void CalendarModel::setMaximumDate(const QDate &date)
{
    if (!date.isValid() || date == m_maximumDate) {
        return;
    }
    m_maximumDate = date;
    if (m_minimumDate > m_maximumDate) {
        setMinimumDate(m_maximumDate);
    }
    if (m_date > m_maximumDate) {
        setDate(m_maximumDate);
    }
}

void CalendarModel::setFirstColumnDay(Qt::DayOfWeek dayOfWeek)
{
    if (m_firstDay == dayOfWeek) {
        return;
    }
    m_firstDay = dayOfWeek;
    internalUpdate();
}

void CalendarModel::setHorizontalHeaderFormat(HORIZONTAL_HEADER_FORMAT format)
{
    if (m_horizontalHeaderFormat == format) {
        return;
    }
    int oldFormat = m_horizontalHeaderFormat;
    m_horizontalHeaderFormat = format;
    if (oldFormat == NO_HORIZONTAL_HEADER) {
        m_firstRow = 1;
        insertRow(0);
    } else if (m_horizontalHeaderFormat == NO_HORIZONTAL_HEADER) {
        m_firstRow = 0;
        removeRow(0);
    }
    internalUpdate();
}

void CalendarModel::setWeekNumbersShown(bool shown)
{
    if (m_isWeekNumbersShown == shown) {
        return;
    }
    if (m_isWeekNumbersShown) {
        m_firstColumn = 1;
        insertColumn(0);
    } else {
        m_firstColumn = 0;
        removeColumn(0);
    }
    internalUpdate();
}

void CalendarModel::showNextMonth()
{
    int year = shownYear();
    int month = shownMonth();
    if (month == 12) {
        ++year;
        month = 1;
    } else {
        ++month;
    }
    showMonth(year, month);
}

void CalendarModel::showPrevMonth()
{
    int year = shownYear();
    int month = shownMonth();
    if (month == 1) {
        --year;
        month = 12;
    } else {
        --month;
    }
    showMonth(year, month);
}

void CalendarModel::showNextYear()
{
    int year = shownYear();
    int month = shownMonth();
    ++year;
    showMonth(year, month);
}

void CalendarModel::showPrevYear()
{
    int year = shownYear();
    int month = shownMonth();
    --year;
    showMonth(year, month);
}

QModelIndex CalendarModel::cellForDate(const QDate &date) const
{
    const QDate refDate = referenceDate();
    if (!refDate.isValid() || !date.isValid()) {
        qDebug() << "NOT VALID!!!";
        return QModelIndex();
    }

    const int columnForFirstOfShownMonth = columnForFirstOfMonth(refDate);
    const int requestedPosition = refDate.daysTo(date) - m_firstColumn + columnForFirstOfShownMonth + refDate.day() - 1;

    int column = requestedPosition % 7;
    int row = requestedPosition / 7;
    if (column < 0) {
        column += 7;
        row -= 1;
    }

    if ((columnForFirstOfShownMonth - m_firstColumn) < MIN_DAY_OFFSET) {
        row += 1;
    }

    if (row < 0
        || row > (ROW_COUNT - 1)
        || column < 0
        || column > (COLUMN_COUNT - 1))
    {
        qDebug() << "NOT VALID 2222!!!";
        return QModelIndex();
    }

    return index(row + m_firstRow, column + m_firstColumn);
}

QDate CalendarModel::referenceDate() const
{
    int refDay = 1;
    while (refDay <= 31) {
        QDate refDate(m_shownYear, m_shownMonth, refDay);
        if (refDate.isValid())
            return refDate;
        refDay += 1;
    }
    return QDate();
}

//QTextCharFormat CalendarModel::formatForCell(int row, int col) const
//{
//    QPalette pal;
//    QPalette::ColorGroup cg = QPalette::Active;
//    if (m_view) {
//        pal = m_view->palette();
//        if (!m_view->isEnabled())
//            cg = QPalette::Disabled;
//        else if (!m_view->isActiveWindow())
//            cg = QPalette::Inactive;
//    }

//    QTextCharFormat format;
//    format.setFont(m_view->font());
//    bool header = (m_horizontalHeaderFormat != CalendarWidget::NoHorizontalHeader && row == HEADER_ROW);
//    format.setBackground(pal.brush(cg, header ? QPalette::AlternateBase : QPalette::Base));
//    format.setForeground(pal.brush(QPalette::Text));
////    Qt::DayOfWeek dayOfWeek = dayOfWeekForColumn(col);

////    if (dayOfWeek == Qt::Saturday || dayOfWeek == Qt::Sunday)
////        format.setForeground(QColor(options->clHolidayTx));
////    else if (!header)
////        format.setForeground(QColor(options->clWorkdayTx));

////    if (col >= m_firstColumn && col < columnCount()) {
////        if (m_dayFormats.contains(dayOfWeek))
////            format.merge(m_dayFormats.value(dayOfWeek));
////    }

////    if (!header) {
////        QDate date = dateForCell(row, col);
////        format.merge(m_dateFormats.value(date));
////        if (date < m_minimumDate || date > m_maximumDate)
////            format.setBackground(QColor(options->clDisableBg));
////        if (m_shownMonth != date.month())
////            format.setForeground(QColor(options->clDisableTx));
////    }
//    return format;
//}
